import GC from '@grapecity/spread-sheets';
import '@grapecity/spread-sheets-charts';
import '@grapecity/spread-sheets/styles/gc.spread.sheets.excel2013white.css';
import { IO } from '@grapecity/spread-excelio'
import { saveAs } from 'file-saver';
import {
    checkStrategy
} from './import/checkArch';
import { v4 as uuidv4 } from 'uuid';
import { getCharNumInString } from '@/pages/biz-subcontract/utils/helper';
import { relativeTimeThreshold } from 'moment';


const DEFAULT_CONFIG: any = {
    blockedBgColor: '#eff3f8'
}

type hAlignType =
    'center'
    | 'left'
    | 'right'

type vAlignType =
    'center'
    | 'bottom'
    | 'top'

type BorderStyle =
    'dashDot'
    | 'dashDotDot'
    | 'dashed'
    | 'dotted'
    | 'double'
    | 'empty'
    | 'hair'
    | 'medium'
    | 'mediumDashDot'
    | 'mediumDashDotDot'
    | 'mediumDashed'
    | 'slantedDashDot'
    | 'thick'
    | 'thin'

type ImageLayout =
    'center'
    | 'none'
    | 'stretch'
    | 'zoom'

interface styleConfig {
    [key: string]: any,
    color?: string,
    font?: string,
    hAlign?: hAlignType,
    vAlign?: vAlignType
    backgroundColor?: string,
    backgroundImage?: string,
    backgroundImageLayout?: ImageLayout,
    border?: string,
    borderLeft?: string,
    borderRight?: string,
    borderTop?: string,
    borderBottom?: string,
    formatter?: string,
    locked?: boolean,
    cellPadding?: string
}

const colorNameMap = {
    red: 'red',
    blue: 'blue',
    aqua: '#00ffff',
    black: '#000000',
    fuchsia: '#ff00ff',
    gray: '#808080',
    green: 'green',
    lime: '#00ff00',
    maroon: '#800000',
    navy: '#000080',
    olive: '#808000',
    orange: '#ffa500',
    purple: '#800080',
    silver: '#c0c0c0',
    teal: '#008080',
    white: '#ffffff',
    yellow: '#ffff00'
}

const styleStrategy: any = {
    color(styleInstance: any, val: string) {
        styleInstance.foreColor = val;
    },
    font(styleInstance: any, val: string) {
        styleInstance.font = val;
    },
    hAlign(styleInstance: any, val: hAlignType) {
        const style = GC.Spread.Sheets.HorizontalAlign[val]
        styleInstance.hAlign = style
    },
    vAlign(styleInstance: any, val: vAlignType) {
        const style = GC.Spread.Sheets.VerticalAlign[val]
        styleInstance.hAlign = style
    },
    backgroundColor(styleInstance: any, val: string) {
        styleInstance.backColor = val
    },
    backgroundImage(styleInstance: any, val: string) {
        styleInstance.backgroundImage = val
    },
    backgroundImageLayout(styleInstance: any, val: string) {
        styleInstance.backgroundImageLayout = val
    },
    border(styleInstance: any, val: string) {
        let attr = parseBordetStyleStr(val)
        styleInstance.borderLeft = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style]);
        styleInstance.borderTop = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style]);
        styleInstance.borderRight = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style]);
        styleInstance.borderBottom = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style])
    },
    borderLeft(styleInstance: any, val: string) {
        let attr = parseBordetStyleStr(val)
        styleInstance.borderLeft = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style]);
    },
    borderRight(styleInstance: any, val: string) {
        let attr = parseBordetStyleStr(val)
        styleInstance.borderRight = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style]);
    },
    borderTop(styleInstance: any, val: string) {
        let attr = parseBordetStyleStr(val)
        styleInstance.borderTop = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style]);
    },
    borderBottom(styleInstance: any, val: string) {
        let attr = parseBordetStyleStr(val)
        styleInstance.borderBottom = new GC.Spread.Sheets.LineBorder(attr.color, GC.Spread.Sheets.LineStyle[attr.style]);
    },
    locked(styleInstance: any, val: boolean) {
        styleInstance.locked = val
    },
    cellPadding(styleInstance: any, val: string) {
        styleInstance.cellPadding = val
    }
}

interface borderAttr {
    color: string,
    style: BorderStyle
}

interface gridlineOption {
    color?: string,
    showVerticalGridline?: boolean,
    ShowHorizontalGridline?: boolean,
}

interface protectionOptionsInterface {
    allowSelectLockedCells?: boolean,
    allowSelectUnlockedCells?: boolean,
    allowSort?: boolean,
    allowFilter?: boolean,
    allowEditObjects?: boolean,
    allowResizeRows?: boolean
}

interface sheetOption {
    [key: string]: any,
    allowCellOverflow?: boolean,
    clipBoardOptions?: GC.Spread.Sheets.ClipboardPasteOptions,
    colHeaderAutoText?: GC.Spread.Sheets.HeaderAutoText,
    colHeaderAutoTextIndex?: number,
    colHeaderVisible?: boolean,
    frozenlineColor?: string,
    gridline?: gridlineOption,
    isProtected?: boolean,
    protectionOptions?: protectionOptionsInterface,
    rowHeaderAutoText?: GC.Spread.Sheets.HeaderAutoText,
    rowHeaderAutoTextIndex?: number,
    rowHeaderVisible?: boolean,
    selectionBackColor?: string,
    selectionBorderColor?: string
}

const parseBordetStyleStr = function (borderStyleString: string): borderAttr {
    let ret: borderAttr = {
        color: 'red',
        style: 'double'
    }
    let arr = borderStyleString.split(" ");
    let colorReg = /^#([a-f0-9]{3}){1,2}b/i
    for (let i = 0, len = arr.length; i < len; i++) {
        let item = arr[i]
        if (colorReg.test(item)) {
            ret.color = arr[i]
            arr.splice(i, 1)
            break
        }
    }
    ret.style = (<BorderStyle>arr[0])

    return ret
}

export function createSpreadStyle(config: styleConfig) {
    const styleInstance = new GC.Spread.Sheets.Style()
    Object.keys(config).forEach(e => {
        let item: string = config[e]
        styleStrategy[e](styleInstance, item)
    })
    return styleInstance
}

export function blocked(activeSheet: any, blockedBgColor?: string) {
    let spreadNS = GC.Spread.Sheets;

    const blockStyle = createSpreadStyle({
        locked: true,
        backgroundColor: blockedBgColor ? blockedBgColor : DEFAULT_CONFIG.blockedBgColor
    })
    blockStyle.vAlign = GC.Spread.Sheets.VerticalAlign.center
    blockStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.center
    let clen = activeSheet.getColumnCount()
    let rlen = activeSheet.getRowCount()
    for (let j = 0; j < rlen; j++) {
        for (let i = 0; i < clen; i++) {
            activeSheet.setStyle(j, i, blockStyle, spreadNS.SheetArea.viewport);
        }

    }

    var option = {
        allowSelectLockedCells: true,
        allowSelectUnlockedCells: true
    };
    activeSheet.options.protectionOptions = option;
    activeSheet.options.isProtected = true;
    return activeSheet

}

export function exportExcel(spread: GC.Spread.Sheets.Workbook, name: string) {
    let sheet = spread.getSheet(0);
    sheet.options.isProtected = false;
    let json = spread.toJSON({
        includeBindingSource: true,
        ignoreStyle: false,
        ignoreFormula: false,
        rowHeadersAsFrozenColumns: false,
        columnHeadersAsFrozenRows: false,
    });
    let excelIo = new IO();
    const excelName = `${name || 'xxx'}.xlsx`;
    excelIo.save(json, (blob) => {
        saveAs(blob, excelName);
        sheet.options.isProtected = true;
    }, err => {
        // process error
        console.error(err);
    });
}

//查找器 getcell getrange
export function cellQuery(activeSheet: any, row: number, col: number, rowCount?: number, colCount?: number) {
    return new CellQuery(activeSheet, row, col, rowCount, colCount)
}

class CellQuery {
    activeSheet: any
    cell?: any
    cellRange?: any
    row: number
    col: number

    constructor(activeSheet: any, row: number = 0, col: number = 0, rowCount?: number, colCount?: number) {
        this.activeSheet = activeSheet
        this.row = row
        this.col = col
        if (rowCount == null || colCount == null) {
            this.cell = this.activeSheet.getCell(row, col)
        } else {
            this.cellRange = this.activeSheet.getRange(row, col, rowCount, colCount)
        }
        return this
    }
    getOriginCell() {
        if (this.cell) {
            return this.cell
        } else if (this.cellRange) {
            return this.cellRange
        }
        console.error("没有找到cell")
        return null
    }
    vAlign(val: vAlignType) {
        if (this.cell) {
            let style = createSpreadStyle({
                vAlign: val
            })
            this.activeSheet.setStyle(this.row, this.col, style);
        } else if (this.cellRange) {
            this.cellRange.vAlign(GC.Spread.Sheets.VerticalAlign[val]);
        }
        return this
    }
    hAlign(val: hAlignType) {
        if (this.cell) {
            let style = createSpreadStyle({
                hAlign: val,
                color: "red"
            })
            console.log("##################")
            console.log(this.cell)
            this.activeSheet.setStyle(this.row, this.col, style);
        } else if (this.cellRange) {
            this.cellRange.hAlign(GC.Spread.Sheets.HorizontalAlign[val]);
        }
        return this
    }
}

export function importExcel(spread: GC.Spread.Sheets.Workbook, file: Blob, cb: (spreadJSON: any) => void) {
    let excelIO = new IO();
    //加载文件并转成spreadjs可读的数据
    let activeSheet = spread.getActiveSheet()
    excelIO.open(file, (spreadJSON: any) => {
        if (activeSheet) {
            console.log(spreadJSON)
            activeSheet.reset();
            // spread.fromJSON(spreadJSON);
            cb(spreadJSON)
        }
    })

}

export function Z(spreadInstance: any, options?: sheetOption) {
    return new SpreadWrap(spreadInstance, options)
}

class SpreadWrap {
    spreadInstance: any
    activeSheet: any
    constructor(spreadInstance: any, options?: sheetOption) {
        this.spreadInstance = spreadInstance
        this.activeSheet = spreadInstance.getActiveSheet()
        if (options) {
            Object.keys(options).forEach(key => {
                this.activeSheet[key] = options[key]
            })
        }

    }
    render(cb: any) {
        this.activeSheet.suspendPaint();
        cb.call(this)
        this.activeSheet.resumePaint();
        return this
    }
    cell(row: number, col: number, rowCount?: number, colCount?: number) {
        return cellQuery(this.activeSheet, row, col, rowCount, colCount)
    }
    getActiveSheet() {
        return this.activeSheet
    }

}


enum fmmStatus {
    bigChinaNumber,
    lowerLetter,
    node,
    itemornode
}

type fmmStr = 'bigChinaNumber' | 'lowerLetter' | 'node' | 'itemornode'

//状态从大一到小a到1（node）到item 这三个变化不可以回退 不可以变化超过一步 最后一个状态item可以跳到任意状态
const statusChangerType1 = {
    status: fmmStatus.bigChinaNumber,
    changeStatus(lv: number) {
        if (lv - this.status !== 1) return false
        this.status = lv
        //超过边界归到0
        if (this.status > fmmStatus.itemornode) {
            this.status = 0
        }
    }
}

const checkStrategyMap = [checkStrategy['checkChineseNumber'],
checkStrategy['checkLowerCaseLetters'],
checkStrategy['checkIsIntegerStr'],
checkStrategy['checkNumWithDotInherit']
]

const handleExcelData = [
    (parentItem: any, item: any) => {
        item.id = uuidv4();
        item.parentId = 0;
        item.level = 1;
        item.hasChildren = false;
        if (parentItem.children == null) {
            parentItem.children = []
        }
        parentItem.children.push(item)
    },
    (parentItem: any, item: any) => {
        item.id = uuidv4();
        item.parentId = parentItem.id;
        item.level = 2;
        item.hasChildren = false;
        if (parentItem.children == null) {
            parentItem.children = []
        }
        parentItem.children.push(item)
        parentItem.hasChildren = true
    },
    (parentItem: any, item: any) => {
        item.id = uuidv4();
        item.parentId = parentItem.id;
        item.level = 3;
        item.hasChildren = false;
        if (parentItem.children == null) {
            parentItem.children = []
        }
        parentItem.children.push(item)
        parentItem.hasChildren = true
    },
    (parentItem: any, item: any) => {
        item.id = uuidv4();
        item.parentId = parentItem.id;
        item.level = parentItem.level + 1;
        item.hasChildren = false;
        if (parentItem.children == null) {
            parentItem.children = []
        }
        parentItem.children.push(item)
        parentItem.hasChildren = true
    },

]

//获取一棵树中每个level的节点中最后的哪个
const getLastNodeInTreeBylevel = (tree: any, level: any) => {
    let ret = tree
    for (let i = 0; i < level - 1; i++) {
        ret = ret.children[ret.children.length - 1]
    }
    return ret
}

//格式为大一 =》小a=》 节点1(正整数字符串)=》 节点或项目1.x（可以无限向下接）
export const makeListToExcelDataType1BySerialNo = (list: any) => {
    let excelData = {
        id: 0,
        level: 0,
        children: []
    }
    let parentItem = excelData
    for (let i = 0, len = list.length; i < len; i++) {
        let item = list[i]
        item.serialNo = item[0].value
        let status = statusChangerType1.status
        if (status === fmmStatus.itemornode) {
            //后续节点是正整数节点的子节点
            if (checkStrategyMap[status](item.serialNo, parentItem.serialNo)) {
                handleExcelData[fmmStatus.itemornode](parentItem, item)
                parentItem = item;
            } else {
                //检查不是正整数节点的子节点的类型
                let type = -1
                for (let i = 0; i < checkStrategyMap.length; i++) {
                    let strategy = checkStrategyMap[i](item.serialNo)
                    if (strategy) {
                        type = i
                        break
                    }
                }
                if (type === -1) {
                    if (checkStrategy['checkNumWithDot'](item.serialNo)) {
                        let ItemTypeLevel = 3 + getCharNumInString(item.serialNo, ".")
                        //根据.的个数来确定级别
                        parentItem = getLastNodeInTreeBylevel(excelData, ItemTypeLevel)
                        handleExcelData[fmmStatus.itemornode](parentItem, item)
                        statusChangerType1.changeStatus(type)
                        parentItem = item
                    } else {
                        throw new Error(`序列号${item.serialNo}错误请检查 不是任意一种合法的数据结构`)
                    }
                } else {
                    let ItemTypeLevel = type + 1
                    parentItem = getLastNodeInTreeBylevel(excelData, ItemTypeLevel)
                    handleExcelData[type](parentItem, item)
                    statusChangerType1.changeStatus(type)
                    parentItem = item
                }

            }
        } else {
            if (checkStrategyMap[status](item.serialNo)) {
                handleExcelData[status](parentItem, item)
                statusChangerType1.changeStatus(++status)
                parentItem = item
            } else {
                switch (status) {
                    case 0:
                        throw new Error(`序列号${item.serialNo}错误请检查 大一类型的节点下面必须为小a类型的节点`)
                    case 1:
                        throw new Error(`序列号${item.serialNo}错误请检查 小a类型的节点下面必须为正整数字符串类型（如1）的节点`)
                    case 2:
                        throw new Error(`序列号${item.serialNo}错误请检查 正整数字符串类型（如1）的节点下面必须为项目类型（如1.x, 1.x.x）类型（如1）的节点`)
                }
                //todo error增强咋搞
            }

        }

    }
    return excelData
}

interface SpreandSelectedrowItem {
    row: number,
    rowCount: number,
    col: number,
    colCount: number,
}

export const getAllSelectedRowIndices = (selectedRanges: SpreandSelectedrowItem[]) => {
    // 1、先要对 selectedRanges 里的 对象 按照 row 的大小 进行排序，row 小的排前面
    let sortSelectedRanges: SpreandSelectedrowItem[] = []; // 已经按 row 从小到大 排序好的 selectedRanges

    let selectedRanges_new = [...selectedRanges];

    for (let i = 0; i < selectedRanges.length; i++) {
        let smallestIndex = 0; // 最小的 row 所在的对象 的 索引

        // 找到当前 最小的row 的索引 : r
        for (let r = 0; r < selectedRanges_new.length; r++) {
            if (selectedRanges_new[r].row < selectedRanges_new[smallestIndex].row) {
                smallestIndex = r;
            }
        }

        let smallestItem = selectedRanges_new.splice(smallestIndex, 1)[0];
        sortSelectedRanges.push(smallestItem);
    }


    let rowIndices: number[] = [];

    for (let item of sortSelectedRanges) {
        let i = 0;
        for (let count = item.rowCount; count > 0; count--) {
            rowIndices.push(item.row + i);
            i++;
        }
    }
    // 去重
    return [...new Set(rowIndices)];
};



